package com.example.mybatis.config.impl;

import com.example.mybatis.constant.DataSourceConst;
import com.example.mybatis.constant.TransactionIsolationLevel;
import com.example.mybatis.exception.MybatisException;
import com.example.mybatis.factory.ObjectFactory;
import com.example.mybatis.factory.impl.DefaultObjectFactory;
import com.example.mybatis.handler.type.handler.TypeHandler;
import com.example.mybatis.handler.type.register.TypeHandlerRegister;
import com.example.mybatis.handler.type.register.impl.DefaultTypeHandlerRegister;
import com.example.mybatis.mapper.MapperMethod;
import com.example.mybatis.mapper.MapperRegister;
import com.example.mybatis.plugin.Interceptor;
import com.example.mybatis.support.alias.TypeAliasRegister;
import com.example.mybatis.support.alias.impl.DefaultTypeAliasRegister;
import com.example.mybatis.transaction.Transaction;
import com.example.mybatis.transaction.impl.JdbcTransaction;
import com.example.mybatis.transaction.impl.ManageTransaction;
import com.example.mybatis.utils.XmlUtils;
import com.github.houbb.heaven.util.lang.ObjectUtil;
import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.heaven.util.lang.reflect.ClassUtil;
import com.github.houbb.thread.pool.api.IDataSourceConfig;
import com.github.houbb.thread.pool.bs.JdbcPoolBs;
import com.github.houbb.thread.pool.model.DataSourceConfigDto;
import com.sun.xml.internal.ws.util.xml.XmlUtil;
import org.dom4j.Element;
import sun.nio.cs.ext.MacArabic;

import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

public class XmlConfig extends ConfigAdaptor {
    private final String configPath;
    /**
     * 配置文件信息
     *
     * @since 0.0.1
     */
    private Element root;

    /**
     * 数据源信息
     */
    private DataSource dataSource;
    /**
     * 事务管理器
     */
    private Transaction transaction;
    /**
     * mapper 注册类
     *
     * @since 0.0.1
     */
    private final MapperRegister mapperRegister = new MapperRegister(this);
    /**
     * 别称注册类
     */
    private final TypeAliasRegister typeAliasRegister = new DefaultTypeAliasRegister();

    private final TypeHandlerRegister typeHandlerRegister = new DefaultTypeHandlerRegister();

    /**
     * 对象工厂类
     *
     * @param configPath
     */
    private ObjectFactory objectFactory = new DefaultObjectFactory();
    /**
     * 拦截列表
     *
     * @since 0.0.3
     */
    private final List<Interceptor> interceptorList = new ArrayList<>();


    public XmlConfig(String configPath) {
        this.configPath = configPath;
        //初始化配置信息-----------第一阶段
        initProperties();
        //初始化数据源信息-----------第一阶段
        initDataSource();
        //初始化事务管理器-------第二阶段
        initTransaction();
        //初始化对象工厂---------第二阶段
        initObjectFactory();
        //设置类型别称----------第二阶段
        initTypeAlias();
        //初始化mAPPERX信息-----------第一阶段
        initMapper();
        //拦截器----------第二阶段
        initInterceptorList();
        //类型处理器----------第二阶段
        initTypeHandler();
    }

    private void initTypeHandler() {
        Element mappers = root.element("typeHandlers");
        if (ObjectUtil.isNull(mappers)) {
            return;
        }
        //遍历所有的需要初始化mapper的路径
        for (Object item : mappers.elements("typeHandler")) {
            Element mapper = (Element) item;
            String javaTypeClassName = mapper.attributeValue("javaType");
            //处理别名
            String fullTypeClassName = getTypeAlias(javaTypeClassName);
            Class javaTypeClass = ClassUtil.getClass(fullTypeClassName);
            String handlerClassName = mapper.attributeValue("handler");
            Class handlerClass = ClassUtil.getClass(handlerClassName);
            //创建拦截器实例
            TypeHandler typeHandler = (TypeHandler) this.newInstance(handlerClass);
            typeHandlerRegister.register(javaTypeClass,typeHandler);
        }
    }

    private void initInterceptorList() {
        Element mappers = root.element("plugins");
        if (ObjectUtil.isNull(mappers)) {
            return;
        }
        //遍历所有需要初始化的mapper文件路径
        for (Object item : mappers.elements("plugin")) {
            Element element = (Element) item;
            String value = element.attributeValue("interceptor");
            Class clazz = ClassUtil.getClass(value);

            //创建拦截器实例
            Interceptor interceptor = (Interceptor) this.newInstance(clazz);
            interceptorList.add(interceptor);
        }
    }

    private void initTypeAlias() {
        Element element = root.element("typeAliases");
        //查询所有初始化的mapper文件路径
        if (ObjectUtil.isNull(element)) {
            return;
        }
        for (Object item : element.elements("typeAlias")) {
            Element apper = (Element) item;
            String alias = apper.attributeValue("alias");
            String type = apper.attributeValue("type");
            typeAliasRegister.register(alias, type);
        }
    }

    private void initObjectFactory() {
        Element objectFactory = root.element("objectFactory");
        if (objectFactory != null) {
            String type = objectFactory.attributeValue("type");
            Class<?> clazz = ClassUtil.getClass(type);
            this.objectFactory = (ObjectFactory) ClassUtil.newInstance(clazz);
        }
    }

    private void initTransaction() {
        //根据配置，初始化连接信息
        Element dsElem = root.element("transaction");
        String type = dsElem.attributeValue("type");
        Map<String, String> map = new HashMap<>();
        for (Object property : dsElem.elements("property")) {
            Element element = (Element) property;
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            map.put(name, value);
        }
        TransactionIsolationLevel transactionIsolationLevel = TransactionIsolationLevel.READ_COMMITTED;
        boolean autoCommit = "true".equals(map.get("autoCommit"));
        String isolationLevelStr = map.get("isolationLevel");
        if (StringUtil.isNotEmpty(isolationLevelStr)) {
            transactionIsolationLevel = TransactionIsolationLevel.valueOf(isolationLevelStr);
        }
        //jdbc
        if ("jdbc".equals(type)) {
            this.transaction = new JdbcTransaction(dataSource, transactionIsolationLevel, autoCommit);
        } else {
            this.transaction = new ManageTransaction(dataSource, transactionIsolationLevel);
        }
    }

    private void initProperties() {
        this.root = XmlUtils.getRoot(configPath);
    }

    private void initDataSource() {
        //根据配置文件，初始化对应信息
        Element element = root.element("dataSource");
        DataSourceConfigDto dataSourceConfigDto = new DataSourceConfigDto();
        for (Object property : element.elements("property")) {
            Element elementNode = (Element) property;
            String name = elementNode.attributeValue("name");
            String value = elementNode.attributeValue("value");
            final String key = DataSourceConst.JDBC_PREFIX + name;
            if (DataSourceConst.URL.equals(key)) {
                dataSourceConfigDto.setJdbcUrl(value);
            } else if (DataSourceConst.DRIVER.equals(key)) {
                dataSourceConfigDto.setDriverClass(value);
            } else if (DataSourceConst.USERNAME.equals(key)) {
                dataSourceConfigDto.setUser(value);
            } else if (DataSourceConst.PASSWORD.equals(key)) {
                dataSourceConfigDto.setPassword(value);
            }
        }

//反射构建对象
        String datasourceType = element.attributeValue("type");
        this.dataSource = getDataSource(dataSourceConfigDto, datasourceType);
    }

    /**
     * 获取对应的数据源
     *
     * @param dataSourceConfigDto
     * @param datasourceType
     * @return
     */
    private DataSource getDataSource(final DataSourceConfigDto dataSourceConfigDto, String datasourceType) {
        if (StringUtil.isEmpty(datasourceType)) {
            //后期可以调整为pooled实现
            return JdbcPoolBs.newInstance().username(dataSourceConfigDto.getUser())
                    .password(dataSourceConfigDto.getPassword()).driverClass(dataSourceConfigDto.getDriverClass()).url(dataSourceConfigDto.getJdbcUrl()).pooled();
        }
        //通过反射
        Class<?> dataSourceClass = ClassUtil.getClass(datasourceType);
        //必须继承自IDataSourceConfig
        if (dataSourceClass.isAssignableFrom(IDataSourceConfig.class)) {
            Object instance = ClassUtil.newInstance(dataSourceClass);
            IDataSourceConfig dataSourceConfig = (IDataSourceConfig) instance;
            dataSourceConfig.setUser(dataSourceConfigDto.getUser());
            dataSourceConfig.setPassword(dataSourceConfigDto.getPassword());
            dataSourceConfig.setJdbcUrl(dataSourceConfigDto.getJdbcUrl());
            dataSourceConfig.setDriverClass(dataSourceConfigDto.getDriverClass());
            return dataSourceConfig;
        }
        try {
            //如果不是，那么继续
            Properties properties = new Properties();
            properties.setProperty(DataSourceConst.URL, dataSourceConfigDto.getJdbcUrl());
            properties.setProperty(DataSourceConst.DRIVER, dataSourceConfigDto.getDriverClass());
            properties.setProperty(DataSourceConst.USERNAME, dataSourceConfigDto.getUser());
            properties.setProperty(DataSourceConst.PASSWORD, dataSourceConfigDto.getPassword());

            Constructor<?> constructor = dataSourceClass.getConstructor(Properties.class);
            return (DataSource) constructor.newInstance(properties);
        } catch (Exception e) {
            throw new MybatisException("DataSource class must has public constructor with args properties!");
        }
    }

    /**
     * 初始化mapper信息--第一步
     */
    private void initMapper() {
        Element mappers = root.element("mappers");
        if (ObjectUtil.isNull(mappers)) {
            return;
        }
        //遍历所有的初始化的mapper文件路径
        for (Object item : mappers.elements("mapper")) {
            Element element = (Element) item;
            String path = element.attributeValue("resources");
            mapperRegister.addMapper(path);
        }
    }

    @Override
    public DataSource getDataSource() {
        return this.getDataSource();
    }

    @Override
    public Connection getConnection() {
        return this.transaction.getConnection();
    }

    @Override
    public MapperMethod getMapperMethod(Class clazz, String methodName) {
        return this.mapperRegister.getMapperMethod(clazz, methodName);
    }

    @Override
    public List<Interceptor> getInterceptorList() {
        return this.interceptorList;
    }

    @Override
    public String getTypeAlias(String alias) {
        return this.typeAliasRegister.getTypeOrDefault(alias);
    }

    @Override
    public <T> T newInstance(Class<T> tClass) {
        return this.objectFactory.newInstance(tClass);
    }

    @Override
    public <T> TypeHandler<T> getTypeHandler(Class<T> javaType) {
        return (TypeHandler<T>) this.typeHandlerRegister.getTypeHandler(javaType);
    }
}
